#include "stdafx.h"

#include "TwitterContentHandler.h"
#include "StickyTweetsAppXP.h"
#include "Resource.h"

TwitterContentHandler::TwitterContentHandler(CAtlList<TwitterStatus>& timelineUI) :
  _twitterElement(TwitterElement_None),
  _inUser(FALSE),
  _inRecipient(false),
  _inPlace(FALSE),
  _timelineUI(timelineUI)
{
}

TwitterContentHandler::~TwitterContentHandler()
{
}

HRESULT TwitterContentHandler::startElement( 
            /* [in] */ const wchar_t __RPC_FAR *pwchNamespaceUri,
            /* [in] */ int cchNamespaceUri,
            /* [in] */ const wchar_t __RPC_FAR *pwchLocalName,
            /* [in] */ int cchLocalName,
            /* [in] */ const wchar_t __RPC_FAR *pwchRawName,
            /* [in] */ int cchRawName,
            /* [in] */ ISAXAttributes __RPC_FAR *pAttributes)
{
  if (wcscmp(pwchLocalName, L"created_at") == 0 && !_inUser && !_inRecipient)
  {
    _twitterElement = TwitterElement_Date;
    _date = L"";
  }
  else if (wcscmp(pwchLocalName, L"id") == 0 && !_inUser && !_inRecipient && !_inPlace)
  {
    _twitterElement = TwitterElement_Id;
    _id = L"";
  }
  else if (wcscmp(pwchLocalName, L"text") == 0)
  {
    _twitterElement = TwitterElement_Text;
    _text = L"";
  }
  else if (wcscmp(pwchLocalName, L"source") == 0 && !_inUser && !_inRecipient)
  {
    _twitterElement = TwitterElement_Source;
    _source = L"";
  }
  else if (wcscmp(pwchLocalName, L"in_reply_to_screen_name") == 0 && !_inUser && !_inRecipient)
  {
    _twitterElement = TwitterElement_InReplyToScreenName;
    _inReplyToScreenName = L"";
  }
  else if ((wcscmp(pwchLocalName, L"user") == 0 || wcscmp(pwchLocalName, L"sender") == 0) && !_inUser)
  {
    _inUser = TRUE;
  }
  else if (wcscmp(pwchLocalName, L"recipient") == 0 && !_inRecipient)
  {
    _inRecipient = true;
  }
  else if (wcscmp(pwchLocalName, L"place") == 0)
  {
    _inPlace = TRUE;
  }
  else if (wcscmp(pwchLocalName, L"screen_name") == 0 && _inUser)
  {
    _twitterElement = TwitterElement_ScreenName;
    _screenName = L"";
  }
  else if (wcscmp(pwchLocalName, L"profile_image_url") == 0 && _inUser)
  {
    _twitterElement = TwitterElement_ProfileImageUrl;
    _profileImageUrl = L"";
  }
  else
  {
    _twitterElement = TwitterElement_None;
  }

  return S_OK;
}

HRESULT TwitterContentHandler::characters(const wchar_t* pwchChars, int cchChars)
{
  switch (_twitterElement)
  {
  case TwitterElement_Date:
    _date.Append(pwchChars, cchChars);
    break;

  case TwitterElement_Id:
    _id.Append(pwchChars, cchChars);
    break;

  case TwitterElement_Text:
    _text.Append(pwchChars, cchChars);
    break;

  case TwitterElement_Source:
    _source.Append(pwchChars, cchChars);
    break;

  case TwitterElement_InReplyToScreenName:
    _inReplyToScreenName.Append(pwchChars, cchChars);
    break;

  case TwitterElement_ScreenName:
    _screenName.Append(pwchChars, cchChars);
    break;

  case TwitterElement_ProfileImageUrl:
    _profileImageUrl.Append(pwchChars, cchChars);
    break;
  }

  return S_OK;
}

HRESULT TwitterContentHandler::endElement( 
            /* [in] */ const wchar_t *pwchNamespaceUri,
            /* [in] */ int cchNamespaceUri,
            /* [in] */ const wchar_t *pwchLocalName,
            /* [in] */ int cchLocalName,
            /* [in] */ const wchar_t *pwchRawName,
            /* [in] */ int cchRawName)
{
  _twitterElement = TwitterElement_None;
  if (wcscmp(pwchLocalName, L"status") == 0 || wcscmp(pwchLocalName, L"direct_message") == 0)
  {
    StickyTweetsAppXP& app = StickyTweetsAppXP::GetInstance();

    int compare(0);
    POSITION insertPos(NULL);
    POSITION testPos = _timelineUI.GetHeadPosition();
    while (testPos)
    {
      insertPos = testPos;
      TwitterStatus& compStat = _timelineUI.GetNext(testPos);
      if (_id == compStat.Id)
      {
        //ATLTRACE2(L"Status %s already exisits in list\n", _id);
        return S_OK;
      }

      if (_id.GetLength() < compStat.Id.GetLength() || _id < compStat.Id)
      {
        //ATLTRACE2(L"Insert status %s after %s\n", _id, compStat.Id);
        compare = 1;
      }
      else if (_id.GetLength() > compStat.Id.GetLength() || _id > compStat.Id)
      {
        //ATLTRACE2(L"Insert status %s before %s\n", _id, compStat.Id);
        compare = -1;
        break;
      }
    }

    TwitterStatus status;
    status.CreatedAt.Text = app.FormatDate(_date);
    status.Id = _id;

    status.LinkCount = 0;
    //ATLTRACE2(L"Tweet text: [%s]\n", _text.GetString());
    _text.Replace('\n', ' ');
    _text.Replace(L"&quot;", L"\"");
    _text.Replace(L"&lt;", L"<");
    _text.Replace(L"&gt;", L">");

    LPTSTR textBuffer = _text.GetBuffer();
    INT textLen = _text.GetLength();
    CString unicodeText;

    //TCHAR loByte(L'\0');
    //for (INT i = 0; i < textLen; i++)
    //{
    //  if (i + 8 < textLen && _text.Mid(i, 3) == L"&#x" && _text.Mid(i + 7, 1) == L";")
    //  {
    //    //if (swscanf(textBuffer + i + 3, L"%x", &loByte) == 1) // && swscanf_s(textBuffer + i + 5, L"%02x", &loByte, sizeof(BYTE)) == 1)
    //    if (swscanf(_text.Mid(i + 3, 4).GetBuffer(), L"%x", &loByte) == 1) // && swscanf_s(textBuffer + i + 5, L"%02x", &loByte, sizeof(BYTE)) == 1)
    //    {
    //      //TCHAR unicodeChar = (hiByte << 8) | loByte;
    //      unicodeText.AppendFormat(L"%c", loByte);
    //    }
    //    i += 7;
    //  }
    //  else if (i + 6 < textLen && _text.Mid(i, 3) == L"&#x" && _text.Mid(i + 5, 1) == L";")
    //  {
    //    i += 5;
    //  }
    //  else
    //  {
    //    unicodeText.AppendChar(_text.GetAt(i));
    //  }
    //}

    // TODO: This is slow but works, fix above code (stack warning?!?)
    if (_text.Find(L"&#x") > -1)
    {
      CString unicodeChar;
      for (TCHAR i = 128; i < 256; i++)
      {
        unicodeChar.Format(L"&#x%02x;", i);
        _text.Replace(unicodeChar, CString(i));
      }
      for (TCHAR i = 128; i < 2048; i++)
      {
        unicodeChar.Format(L"&#x%04x;", i);
        _text.Replace(unicodeChar, CString(i));
      }
    }

    status.FullText = _text;

    int firstChar(0), tokStart(0);

    status.NumWords = 0;
    CString word = status.FullText.Tokenize(L" ", tokStart);
    while (word.GetLength() > 0)
    {
      int inWordLinkIdx = max(word.Find(L"@"), word.Find(L"http"));
      if (inWordLinkIdx > 0)
      {
        status.Words[status.NumWords].Link = NoLink;
        status.Words[status.NumWords].Length = inWordLinkIdx;
        status.Words[status.NumWords].First = firstChar;
        firstChar += inWordLinkIdx;
        status.NumWords++;
        word = word.Mid(inWordLinkIdx);
        continue;
      }

      CString invalidLinkChars;

      if (word.Left(1) == L"@" && word.GetLength() > 1)
      {
        status.Words[status.NumWords].Link = UserLink;
        invalidLinkChars = L"?'()!:.,\"";
      }
      else if (word.Left(4) == L"http")
      {
        status.Words[status.NumWords].Link = WebLink;
        invalidLinkChars = L"'()!,";
      }
      else
      {
        status.Words[status.NumWords].Link = NoLink;
      }

      while (status.FullText.GetAt(firstChar) != word.GetAt(0))
        firstChar++;
      status.Words[status.NumWords].First = firstChar;

      if (status.Words[status.NumWords].Link != NoLink)
      {
        int invalidIdx = word.FindOneOf(invalidLinkChars);
        if (invalidIdx >= 0)
        {
          status.Words[status.NumWords].Length = invalidIdx;
          status.NumWords++;
          status.Words[status.NumWords].Link = NoLink;
          status.Words[status.NumWords].First = firstChar + invalidIdx;
          status.Words[status.NumWords].Length = (word.GetLength() + 1) - invalidIdx;
        }
        else
        {
          status.Words[status.NumWords].Length = word.GetLength() + 1;
        }
      }
      else
      {
        status.Words[status.NumWords].Length = word.GetLength() + 1;
      }

      firstChar = tokStart;
      status.NumWords++;
      word = status.FullText.Tokenize(L" ", tokStart);
    }

    if (status.NumWords > 0)
      status.Words[status.NumWords - 1].Length--;

    if (_source.GetLength() > 0)
    {
      _rs.LoadStringW(IDS_REGEX_ANCHOR);
      wregex sourceFinder(_rs);
      wcmatch sourceMatch;

      _rs.LoadStringW(IDS_UI_FROM);
      if (regex_match(_source.GetString(), sourceMatch, sourceFinder))
      {
        if (sourceMatch.size() >= 2)
          status.Source.Text = _rs + _source.Mid(sourceMatch.position(1), sourceMatch.length(1));
        else
          status.Source.Text.LoadStringW(IDS_UI_FROMUNK);
      }
      else
      {
        status.Source.Text = _rs + _source;
      }
    }

    if (_inReplyToScreenName.GetLength() > 0)
    {
      status.InReplyToScreenName.Text.Format(IDSF_TWEET_INREPLYTO, _inReplyToScreenName);
    }

    status.User.ScreenName.Text = _screenName;
    status.User.ProfileImageUrl.Text = _profileImageUrl;

    if (insertPos == NULL)
      _timelineUI.AddHead(status);
    else if (compare < 0)
      _timelineUI.InsertBefore(insertPos, status);
    else if (compare > 0)
      _timelineUI.InsertAfter(insertPos, status);
    return S_OK;
  }
  else if (wcscmp(pwchLocalName, L"user") == 0 || wcscmp(pwchLocalName, L"sender") == 0)
  {
    _inUser = FALSE;
  }
  else if (wcscmp(pwchLocalName, L"recipient") == 0)
  {
    _inRecipient = false;
  }
  else if (wcscmp(pwchLocalName, L"place") == 0)
  {
    _inPlace = FALSE;
  }


  return S_OK;
}

